package com.aosan.website.common.swagger

import SwaggerConfigProperties
import com.google.common.base.Predicate
import com.google.common.base.Predicates
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import springfox.documentation.builders.ApiInfoBuilder
import springfox.documentation.builders.PathSelectors
import springfox.documentation.builders.RequestHandlerSelectors
import springfox.documentation.service.*
import springfox.documentation.spi.DocumentationType
import springfox.documentation.spi.service.contexts.SecurityContext
import springfox.documentation.spring.web.plugins.Docket
import springfox.documentation.swagger2.annotations.EnableSwagger2
import java.util.*

@EnableSwagger2
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(SwaggerConfigProperties::class)
@ConditionalOnProperty(name = ["swagger.enabled"], matchIfMissing = true)
class SwaggerAutoConfiguration {

    /**
     * 默认的排除路径，排除Spring Boot默认的错误处理路径和端点
     */
    private val DEFAULT_EXCLUDE_PATH: List<String> = Arrays.asList("/error", "/actuator/**")
    private val BASE_PATH = "/**"


    @Bean
    fun api(swaggerProperties: SwaggerConfigProperties): Docket? {
        // base-path处理
        swaggerProperties.basePackage = swaggerProperties.basePackage ?: BASE_PATH

        val basePath: MutableList<Predicate<String>?> = ArrayList()
        swaggerProperties.basePath.forEach { path -> basePath.add(PathSelectors.ant(path)) }

        if (swaggerProperties.excludePath.isEmpty()) {
            swaggerProperties.excludePath.addAll(DEFAULT_EXCLUDE_PATH)
        }
        // exclude-path处理
        val excludePath: MutableList<Predicate<String>> = ArrayList<Predicate<String>>()
        swaggerProperties.excludePath.forEach { path -> excludePath.add(PathSelectors.ant(path)) }

        return Docket(DocumentationType.SWAGGER_2)
//                .host(swaggerProperties.host)
                .apiInfo(apiInfo(swaggerProperties))
                .select()
                .apis(RequestHandlerSelectors.basePackage(swaggerProperties.basePackage))
//                .paths(Predicates.and(Predicates.not(Predicates.or(excludePath)), Predicates.or(basePath)))
                .paths(PathSelectors.any())
                .build()
//                .securitySchemes(Collections.singletonList(securitySchema(swaggerProperties)))
//                .securityContexts(Collections.singletonList(securityContext(swaggerProperties)))
//                .pathMapping("/")
    }

    /**
     * 配置默认的全局鉴权策略的开关，通过正则表达式进行匹配；默认匹配所有URL
     *
     * @return
     */
    private fun securityContext(swaggerProperties: SwaggerConfigProperties): SecurityContext? {
        return SecurityContext.builder()
                .securityReferences(defaultAuth(swaggerProperties))
                .forPaths(PathSelectors.regex(swaggerProperties.authorization.authRegex))
                .build()
    }

    /**
     * 默认的全局鉴权策略
     *
     * @return
     */
    private fun defaultAuth(swaggerProperties: SwaggerConfigProperties): List<SecurityReference?>? {
        val authorizationScopeList: ArrayList<AuthorizationScope> = ArrayList<AuthorizationScope>()
        swaggerProperties.authorization.authorizationScopeList.forEach { authorizationScope -> authorizationScopeList.add(AuthorizationScope(authorizationScope.scope, authorizationScope.description)) }
        val authorizationScopes: Array<AuthorizationScope?> = arrayOfNulls<AuthorizationScope>(authorizationScopeList.size)
        return Collections.singletonList(SecurityReference.builder()
                .reference(swaggerProperties.authorization.name)
                .scopes(authorizationScopeList.toArray(authorizationScopes))
                .build())
    }


    private fun securitySchema(swaggerProperties: SwaggerConfigProperties): OAuth? {
        val authorizationScopeList: ArrayList<AuthorizationScope> = ArrayList<AuthorizationScope>()
        swaggerProperties.authorization.authorizationScopeList.forEach { authorizationScope -> authorizationScopeList.add(AuthorizationScope(authorizationScope.scope, authorizationScope.description)) }
        val grantTypes = ArrayList<GrantType>()
        swaggerProperties.authorization.tokenUrlList.forEach { tokenUrl -> grantTypes.add(ResourceOwnerPasswordCredentialsGrant(tokenUrl)) }
        return OAuth(swaggerProperties.authorization.name, authorizationScopeList, grantTypes)
    }

    private fun apiInfo(swaggerProperties: SwaggerConfigProperties): ApiInfo? {
        return ApiInfoBuilder()
                .title(swaggerProperties.title)
                .description(swaggerProperties.description)
                .license(swaggerProperties.license)
                .licenseUrl(swaggerProperties.licenseUrl)
                .termsOfServiceUrl(swaggerProperties.termsOfServiceUrl)
                .contact(Contact(swaggerProperties.contact.name, swaggerProperties.contact.url, swaggerProperties.contact.email))
                .version(swaggerProperties.version)
                .build()
    }
}